#include <iostream>
#include <dlfcn.h>

#include "version.h"
#include "tablefuncs.h"
#include "cheader.h"
#include "index.h"
#include "depend.h"
#include "operatingsystem.h"

#include "../Common/include/symboltable.h"
#include "../Common/anyoption/anyoption.h"

string executable;
bool verbose = false;
string limit = "";

typedef SymbolTable* TableFunc(void);
typedef NameList* DependFunc(void);

/* TODO flag 'depend' moet de eigen dependencies opgeven.
*/

void setAnyOption(AnyOption &opt, int argc, char *argv[])
{
    //opt.setVerbose();
    //opt.autoUsagePrint(true);
    
    opt.addUsage(string("Usage: " + string(APPLICATION) + " [options] file").c_str());
    opt.addUsage("Options:");
    opt.addUsage("  -h  --help              Prints this information");
    opt.addUsage("  -V  --version           Print version information");
    opt.addUsage("      --exports           Print module exports");
    opt.addUsage("      --limit <name>      Limits the output to a specific name");
    opt.addUsage("  -d  --depend            Print module dependencies");
    opt.addUsage("      --install <path>    Install the library in the given location");
    opt.addUsage("      --cheader           Outputs a c header file for the FFI");
    opt.addUsage("      --symboltable       Outputs a parsable symboltable");
    opt.addUsage("      --cache             Print module exports for use with the linker cache");
    opt.addUsage("      --paths             Print known paths of module locations");
    opt.addUsage("  -i  --index             (re)build module index");
    opt.addUsage("      --where-is <name>   Prints the name of the module that defines <name>");
    opt.addUsage("  -v  --verbose           Turn on verbosity");
    opt.addUsage("");
    opt.addUsage("For bug reporting instructions, please see:");
    opt.addUsage("<http://www.thinder.org/bugs>");
    
    /* by default all  options  will be checked on the command line and from option/resource file */
    opt.setFlag("help", 'h');
    opt.setFlag("version", 'V');
    opt.setFlag("exports");
    opt.setFlag("cheader");
    opt.setFlag("cache");
    opt.setFlag("index", 'i');
    opt.setOption("where-is");
    opt.setOption("limit");
    opt.setOption("install");
    opt.setFlag("verbose", 'v');
    opt.setFlag("depend", 'd');
    opt.setFlag("symboltable");
    opt.setFlag("paths");
    
    /* go through the command line and get the options  */
    opt.processCommandArgs(argc, argv);
}

int main(int argc, char* argv[]) {
    using std::cout;
    using std::cerr;
    
    executable = argv[0];
    
    AnyOption opt;
    setAnyOption(opt, argc, argv);

    if (!opt.hasOptions() || opt.getFlag("help") || opt.getFlag('h'))
    {
        opt.printUsage();
        return EXIT_SUCCESS;
    }
    
    if (opt.getFlag("version") || opt.getFlag('V'))
    {
        std::cout << string(APPLICATION) + " (" + string(VERSION_INFORMATION) + ") " + string(MAJOR) + "." + string(MINOR) + "." + string(BUILD) << std::endl;
        std::cout << "Last modification on " + string(__TIMESTAMP__) + ", compiled on " + string(__DATE__) + " " + string(__TIME__) << std::endl;
        std::cout << "Part of the Experimental Thinder Compiler Suite. Copyright (c) 2010 Bas du Pre." << std::endl;
        std::cout << "This is free software; see the source for copying conditions.  There is NO\nwarranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." << std::endl;
        std::cout << std::endl;
        return EXIT_SUCCESS;
    }
    
    if (opt.getFlag("verbose") || opt.getFlag('v'))
    {
        verbose = true;
    }
    
    if (opt.getValue("limit") != NULL)
    {
        limit = string(opt.getValue("limit"));
    }
    
    if (opt.getValue("where-is") != NULL)
    {
        string name = opt.getValue("where-is");
        whereIs(name);
        return EXIT_SUCCESS;
    }
    
    if (opt.getFlag("index") || opt.getFlag('i'))
    {
        buildIndex();
        return EXIT_SUCCESS;
    }
    
    if (opt.getFlag("paths"))
    {
        printPaths();
        return EXIT_SUCCESS;
    }
    
    if (opt.getArgc() > 1)
    {
        cerr << string(APPLICATION) + ": more than one file provided" << std::endl; 
        return EXIT_FAILURE;
    } else if (opt.getArgc() == 0) {
        cerr << string(APPLICATION) + ": missing filename" << std::endl; 
        return EXIT_FAILURE;
    }
    
    string inputfile;
    inputfile = opt.getArgv(0);
    
    // open the library
    void* handle = dlopen(inputfile.c_str(), RTLD_LAZY);
    
    if (!handle) {
        cerr << string(APPLICATION) + ": cannot open module: " << inputfile << std::endl;
        cerr << dlerror() <<std::endl;
        return EXIT_FAILURE;
    }
    
    // reset errors
    dlerror();
    TableFunc* tableFunc = (TableFunc*) dlsym(handle, "t_int_getTable");
    DependFunc* dependFunc = (DependFunc*) dlsym(handle, "t_int_getDepend");
    const char *dlsym_error = dlerror();
    if (dlsym_error) {
        cerr << string(APPLICATION) + ": module " << inputfile << " does not appear to be a thinder module" << std::endl;
        cerr << dlerror() <<std::endl;
        dlclose(handle);
        return 1;
    }
    SymbolTable *table = tableFunc();
    
    if (opt.getValue("install") != NULL)
    {
        string location = opt.getValue("install");
        if (location.substr(location.length() - 1)[0] != PATH_SEPARATOR)
            location += PATH_SEPARATOR;
        string soname = exec("objdump -p \"" + inputfile + "\" | grep SONAME");
        soname = soname.substr(9);
        trim(soname);
        exec("cp \"" + inputfile + "\" \"" + location + soname + "\"");
        std::cerr << "installed " + inputfile + " to " + location + soname + "\n"; // TODO error checking (access rights)
        return EXIT_SUCCESS;
    }

    if (opt.getFlag("exports"))
    {
        printTable(table);
        return EXIT_SUCCESS;
    }
    
    if (opt.getFlag("cheader"))
    {
        printCHeader(table);
        return EXIT_SUCCESS;
    }
    
    if (opt.getFlag("cache"))
    {
        printTableForCache(table);
        return EXIT_SUCCESS;
    }
    
    if (opt.getFlag("symboltable"))
    {
        printSymbolTable(table);
        return EXIT_SUCCESS;
    }
    
    if (opt.getFlag("depend"))
    {
        printDepend(dependFunc());
        return EXIT_SUCCESS;
    }
    
    
    
    dlclose(handle);
}

